/*
 * Data: 2022/ 4/ 17
 * Author: CBH37
 * Version: 0.1
 * 
 * Introduction:
 * 林雅欣（小林林）和李欣一（欣欣子）都是很棒的女孩子，
 * 我都很喜欢她们，为此建立起了`lxy`项目，
 * 但是此项目出师未捷身先死，并没有取得成功。
 * 
 * 这几天我认识了一个九班的姑娘徐蕊，正好发愁项目起什么名字好，
 * 于是将她们三人名字的首字母合并在一起作为项目的名字，再通过两天的奋战，
 * 于是便有了这个项目L（LiXinYi）L（LinYaxin）X（XuRui）
 *
 * 至此，从开始接触编程的2020年到如今两年，我为此摸爬滚打写了无数次无数行代码都未实现的能有一个自己的编程语言的梦想终于成真！
 * 
 */
class llx {
    tokenizer(text) {
        let ops = {
            "**" : 14,
            ">>>" : 11,  "<<" : 11,  ">>" : 11,
            "<=" : 10,  ">=" : 10,
            "==" : 9,  "!=" : 9,
            "&&" : 5,
            "||" : 4,

            "*" : 13,  "/" : 13,  "%" : 13,
            "+" : 12,  "-" : 12,
            "<" : 10,  ">" : 10,  
            "&" : 8, 
            "^" : 7,
            "|" : 6,

            /* - 1: 特殊符号的标志 */
            "(" : - 1,  "{" : - 1,
        };

        let keywords = [
            "=",  ',',
            "[",  "]",
            ")",  "}",
            ";",

            "let",  "const",
            "if",  "else",
            "for",  "while",  "loop",
            "break",  "continue",
            "fn",  "return",
        ];
        
        let index = 0;
        function is_end() {
            if(index >= text.length) {
                return true;
            }
        }

        function try_next(string) {
            let old_index = index;
            for(let c of string) {
                if(c != text[index ++]) {
                    index = old_index;
                    return false;
                }
            }

            return true;
        }

        let tokens = [];
        function create_token(type, value) {
            tokens.push({ type, value });
        }

        LOOP:while(! is_end()) {
            /* 单行注释 */
            if(text[index] == '-' && text[index + 1] == '-') {
                index += 2;
                while(! is_end() && text[index] != '\n') { 
                    index ++;
                }

                index ++;   // 越过'\n'
                continue LOOP;
            }

            /* 多行注释 */
            if(text[index] == '/' && text[index + 1] == '*') {
                index += 2;
                while(! is_end() &&
                      !(text[index] == '*' && ! is_end() &&text[index + 1] == '/')) {
                    index ++;
                }

                index += 2; // 跳过注释末尾
                continue LOOP;
            }

            if(text[index].match(/[0-9]/)) {
                let num = '';

                /* INT */
                while(! is_end() && text[index].match(/[0-9]/)) {
                    num += text[index ++];
                }

                /* DOUBLE */
                if(text[index] == '.') {
                    index ++;
                    num += '.';
                    while(! is_end() && text[index].match(/[0-9]/)) {
                        num += text[index ++];
                    }
                }

                create_token("num", parseFloat(num));
                continue LOOP;
            }

            if(text[index] == '"') {
                index ++;
                let string = "";
                while(text[index] != '"') {
                    /* 转义字符 */
                    if(text[index] == '\\') {
                        index ++;
                    }
                    string += text[index ++];
                }

                index ++;
                create_token("string", string);
                continue LOOP;
            }

            /* 处理运算符 */
            for(let op in ops) {
                if(try_next(op)) {
                    create_token("op", op);
                    continue LOOP;
                }
            }

            /* 处理关键字 */
            for(let keyword of keywords) {
                if(try_next(keyword)) {
                    create_token("keyword", keyword);
                    continue LOOP;
                }
            }

            /* 处理标识符 */
            if(text[index].match(/[a-zA-Z$_]/)) {
                let ident = '';
                while(! is_end() && text[index].match(/[0-9a-zA-Z$_.]/)) {
                    ident += text[index ++];
                }

                create_token("ident", ident);
                continue LOOP;
            }

            index ++;
        }

        return tokens;
    }

    parser(tokens) {
        let index = 0;
        function is_end() {
            if(index >= tokens.length) {
                return true;
            }

            return false;
        }

        function next_token(target) {
            if(tokens[index].type != target) {
                throw "Syntax error";
            }
            return tokens[index ++].value;
        }

        function have_token(target) {
            if(tokens[index].value != target) {
                throw "Syntax error";
            }
            return tokens[index ++].type;
        }

        function expr() {
            let op = [];
            let op_code = [];
            let ops = {
                "**" : 14,
                ">>>" : 11,  "<<" : 11,  ">>" : 11,
                "<=" : 10,  ">=" : 10,
                "==" : 9,  "!=" : 9,
                "&&" : 5,
                "||" : 4,
    
                "*" : 13,  "/" : 13,  "%" : 13,
                "+" : 12,  "-" : 12,
                "<" : 10,  ">" : 10,  
                "&" : 8, 
                "^" : 7,
                "|" : 6,

                /* - 1: 特殊符号的标志 */
                "(" : - 1,  "{" : - 1,
            };

            /* 处理一元符号 */
            if(tokens[index].type == "op" &&
               ops[tokens[index].value] != - 1) {
                op_code.push({
                    type : "unary_computation",
                    symbol : tokens[index ++].value,
                    target : tokens[index ++],
                });
            }

            while(! is_end() && tokens[index].type != "keyword") {
                if(tokens[index].value == '(') {
                    have_token('(');
                    op_code.push({
                        type : "calc_block",
                        value : expr(),
                    });
                    have_token(')');
                    continue;
                }

                if(tokens[index].value == '{') {
                    have_token('{');
                    let list = [];
                    let obj = {};
                    while(true) {
                        let item = expr();
                        /* obj 系 */
                        if(Object.keys(item).length == 2 &&
                          (Object.values(item)[0] == "ident" || Object.values(item)[0] == "num" ) &&
                           tokens[index].value == "=") {
                            have_token("=");
                            obj[Object.values(item)[1]] = expr();
                        } else {
                            list.push(item);
                        }
                        
                        if(tokens[index].value == ',') {
                            have_token(',');
                        } else if(tokens[index].value == '}') {
                            break;
                        } else {
                            throw "Syntax error";
                        }
                    }

                    have_token('}')
                    op_code.push({
                        type : "table",
                        value : {...list, ...obj},
                    });
                    continue;
                }

                if(tokens[index].type == "op") {
                    /* 1. 先清栈 */
                    if(op.length > 0 &&
                       ops[tokens[index].value] <= ops[op[op.length - 1]]) {
                        /* 操作数应该比操作符的数量多一 */
                        if(op.length != op_code.length - 1) {
                            throw "the expression of the error";
                        }

                        while(! (op.length >= 2 && ops[op[op.length - 1]] > ops[op[op.length - 2]]) &&
                              op.length != 0) {
                            op_code.push({
                                type : op.pop(),
                                right_val : op_code.pop(),
                                left_val : op_code.pop(),
                            });
                        }
                    }

                    /* 2. 再添加元素 */
                    op.push(tokens[index ++].value);
                } else if(tokens[index].type == "ident") {
                    let ident = next_token("ident");
                    if(tokens[index].value == '[') {
                        have_token('[');
                        op_code.push({
                            type : "access_table",
                            table_name : ident,
                            index : expr(),
                        });
                        have_token(']');
                    } else if(tokens[index].value == '(') {
                        have_token('(');

                        let args = [];
                        while(true) {
                            args.push(expr());
                            if(tokens[index].value == ',') {
                                have_token(',');
                            } else if(tokens[index].value == ')') {
                                break;
                            } else {
                                throw "Syntax error";
                            }
                        }
                        have_token(')');    

                        op_code.push({
                            type : "fn_call",
                            fn_name : ident,
                            args,
                        });
                    } else {
                        op_code.push({
                            type : "ident",
                            ident_name : ident,
                        });
                    }
                } else {
                    op_code.push(tokens[index ++]);
                }
            }

            /* 最后来一波收割 */
            if(op.length != op_code.length - 1) {
                throw "the expression of the error";
            }

            while(op.length != 0) {
                op_code.push({
                    type : op.pop(),
                    right_val : op_code.pop(),
                    left_val : op_code.pop(),
                });
            }
            return op_code[0];
        }

        function parser_core() {
            let ast = [];
            while(! is_end() && tokens[index].value != '}') {
                /* 变量和常量 */
                if(tokens[index].value == "let") {
                    have_token("let");
                    let var_name = next_token("ident");
                    if(tokens[index].value == ';') {
                        have_token(';');
                        ast.push({
                            type : "let_def",
                            var_name,
                        });
                    } else if(tokens[index].value == '=') {
                        have_token('=');
                        ast.push({
                            type : "let",
                            var_name,
                            value : expr(),
                        });
                    } else {
                        throw "Syntax error";
                    }

                    continue;
                }

                if(tokens[index].value == "const") {
                    have_token("const");
                    let const_name = next_token("ident");
                    have_token("=");
                    ast.push({
                        type : "const",
                        const_name,
                        value : expr(),
                    })

                    continue;
                }

                /* 赋值 */
                if(tokens[index].type == "ident") {
                    let ident = tokens[index].value;
                    if(tokens[index + 1].value == '=') {
                        index += 2;
                        ast.push({
                            type : "assignment",
                            ident,
                            value : expr(),
                        });

                        continue;
                    }
                }

                /* 判断语句 */
                if(tokens[index].value == "if") {
                    have_token("if");
                    have_token("(");
                    let if_expr = expr();
                    have_token(')');

                    have_token('{');
                    let if_cont = parser_core();
                    have_token('}');

                    let else_cont = [];
                    if(! is_end() && tokens[index].value == "else") {
                        have_token("else");
                        have_token("{");
                        else_cont = parser_core();
                        have_token('}');
                    }

                    ast.push({
                        type : "if",
                        if_expr,
                        if_cont,
                        else_cont,
                    });

                    continue;
                }

                /* 循环语句 */
                if(tokens[index].value == "for") {
                    have_token("for");
                    have_token("(");
                    let loop_var = next_token("ident");
                    have_token("=");
                    let start = expr();
                    have_token(",");
                    let end = expr();
                    have_token(",");
                    let step = expr();
                    have_token(")");

                    have_token("{");
                    let for_cont = parser_core();
                    have_token("}");

                    ast.push({
                        type : "for",
                        loop_var,
                        start, end, step,
                        for_cont,
                    });
                    continue;
                }

                if(tokens[index].value == "while") {
                    have_token("while");
                    have_token("(");
                    let while_expr = expr();
                    have_token(')');

                    have_token('{');
                    let while_cont = parser_core();
                    have_token('}');

                    ast.push({
                        type : "while",
                        while_expr,
                        while_cont,
                    });
                    continue;
                }

                if(tokens[index].value == "loop") {
                    have_token("loop");
                    have_token("{");
                    ast.push({
                        type : "loop",
                        loop_cont : parser_core(),
                    });
                    have_token("}");

                    continue;
                }

                if(tokens[index].value == "break" ||
                    tokens[index].value == "continue") {
                    ast.push({
                        type : tokens[index].value,
                    });

                    index ++;
                    continue;
                }

                /* 函数 */
                if(tokens[index].value == "fn") {
                    have_token("fn");
                    let fn_name = next_token("ident");
                    have_token('(');
                    let args = [];
                    while(true) {
                        args.push(expr());
                        if(tokens[index].value == ',') {
                            have_token(',');
                        } else if(tokens[index].value == ')') {
                            break;
                        } else {
                            throw "Syntax error";
                        }
                    }
                    have_token(')');

                    have_token('{');
                    let fn_cont = parser_core();
                    have_token('}');

                    ast.push({
                        type : "fn_def",
                        fn_name,
                        args,
                        fn_cont,
                    });
                    continue;
                }

                if(tokens[index].value == "return") {
                    have_token("return");
                    ast.push({
                        type : "return",
                        return_val : expr(),
                    })
                    continue;
                }

                /* The end */
                if(tokens[index].value == ';') {
                    index ++;
                    continue;
                }

                ast.push({
                    type : "expr",
                    expr : expr(),
                });
            }

            return ast;
        }

        /* main */
        return parser_core();
    }

    generate_code(ast) {
        let tab_num = - 1;
        let ops = {
            "**" : 14,
            ">>>" : 11,  "<<" : 11,  ">>" : 11,
            "<=" : 10,  ">=" : 10,
            "==" : 9,  "!=" : 9,
            "&&" : 5,
            "||" : 4,

            "*" : 13,  "/" : 13,  "%" : 13,
            "+" : 12,  "-" : 12,
            "<" : 10,  ">" : 10,  
            "&" : 8, 
            "^" : 7,
            "|" : 6,

            /* - 1: 特殊符号的标志 */
            "(" : - 1,  "{" : - 1,
        };

        function expr_generate(ast) {
            let code_buffer = "";

            switch(ast.type) {
                case "unary_computation": {
                    code_buffer += `${ast.symbol} ${expr_generate(ast.target)}`;
                    break;
                }

                case "calc_block" : {
                    code_buffer += `( ${expr_generate(ast.value)} )`;
                    break;
                }

                case "table": {
                    code_buffer += "{";
                    for(let i in ast.value) {
                        code_buffer += `"${i}" : ${expr_generate(ast.value[i])}, `;
                    }
                    code_buffer += "}";

                    break;
                }

                case "access_table": {
                    code_buffer += `${ast.table_name}[${expr_generate(ast.index)}]`;
                    break;
                }

                case "fn_call" : {
                    code_buffer += `${ast.fn_name}(`;
                    for(let arg of ast.args) {
                        code_buffer += `${expr_generate(arg)}, `;
                    }
                    code_buffer += ")";

                    break;
                }

                case "string" : {
                    code_buffer += `"${ast.value}"`;
                    break;
                }

                case "num" : {
                    code_buffer += ast.value;
                    break;
                }

                case "ident": {
                    code_buffer += ast.ident_name;
                    break;
                }

                default: {
                    if(ast.type in ops) {
                        code_buffer += `${expr_generate(ast.left_val)} ${ast.type} ${expr_generate(ast.right_val)}`;
                    } else {
                        throw `no ${ast.type} type`;
                    }
                }
            }

            return code_buffer;
        }

        function generate_core(ast) {
            let code_buffer = "";

            tab_num ++;
            function make_tab() {
                for(let i = 0; i < tab_num; i ++) {
                    code_buffer += "    ";
                }
            }

            for(let item of ast) {
                make_tab();
                switch(item.type) {
                    case "let_def": {
                        code_buffer += `let ${item.var_name}; \n`;
                        break;
                    }

                    case "let": {
                        code_buffer += `let ${item.var_name} = ${expr_generate(item.value)}; \n`;
                        break;
                    }

                    case "const": {
                        code_buffer += `const ${item.const_name} = ${expr_generate(item.value)}; \n`;
                        break;
                    }

                    case "assignment": {
                        code_buffer += `${item.ident} = ${expr_generate(item.value)}; \n`;
                        break;
                    }

                    case "if": {
                        code_buffer += `if(${expr_generate(item.if_expr)}) {
${generate_core(item.if_cont)}} else {
${generate_core(item.else_cont)}} \n`;
                        break;
                    }

                    case "for": {
                        code_buffer += `for(${item.loop_var} = ${expr_generate(item.start)}; ${item.loop_var} != ${expr_generate(item.end)}; ${item.loop_var} += (${expr_generate(item.step)})) {
${generate_core(item.for_cont)}} \n`;
                        break;
                    }

                    case "while": {
                        code_buffer += `while(${expr_generate(item.while_expr)}) {
${generate_core(item.while_cont)}} \n`;
                        break;
                    }

                    case "loop": {
                        code_buffer += `while(true) {
${generate_core(item.loop_cont)}} \n`;
                        break;
                    }

                    case "break": {
                        code_buffer += "break; \n";
                        break;
                    }

                    case "continue": {
                        code_buffer += "continue; \n";
                        break;
                    }

                    case "fn_def": {
                        code_buffer += `function ${item.fn_name} (`;
                        for(let arg of item.args) {
                            code_buffer += `${expr_generate(arg)}, `;
                        }
                        code_buffer += `) {
${generate_core(item.fn_cont)}} \n`;

                        break;
                    }

                    case "return": {
                        code_buffer += `return ${expr_generate(item.return_val)}; \n`;
                        break;
                    }

                    case "expr": {
                        code_buffer += `${expr_generate(item.expr)}; \n`;
                        break;
                    }
                }
            }

            tab_num --;
            make_tab();
            return code_buffer;
        }

        return generate_core(ast);
    }
}

/* main */
const fs = require('fs')
const args = process.argv.slice(2)
for(arg of args) {
    let output = arg.split('.');
    output = output[0] + ".js";

    fs.readFile(arg, 'utf8' , (err, data) => {
        if(err) {
            throw err;
        }
        
        let llx_obj = new llx;
        let tokens = llx_obj.tokenizer(data);
        let ast = llx_obj.parser(tokens);
        let target = llx_obj.generate_code(ast);

        fs.writeFile(output, target, err => {
            if(err) {
                throw err;
            }
        });
    });
}
